package com.lovegu.two;

import javafx.application.Application;
import javafx.embed.swing.SwingFXUtils;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.Button;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.Slider;
import javafx.scene.image.WritableImage;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import javax.imageio.ImageIO;
import java.io.File;
import java.io.IOException;
import java.util.Stack;

/**
 * <p>
 * 使用 JavaFx 实现画板
 * </p>
 *
 * @author 老顾
 * @since 2023/3/31
 */
public class CanvasForJavaFx extends Application {
    private Canvas canvas;
    private GraphicsContext gc;
    private double lastX, lastY;
    private Color currentColor = Color.BLACK;
    private double currentLineWidth = 5;
    private Stack<WritableImage> undoStack = new Stack<>();
    private Stack<WritableImage> redoStack = new Stack<>();

    @Override
    public void start(Stage primaryStage) throws Exception {
        BorderPane root = new BorderPane();
        canvas = new Canvas(800, 600);
        gc = canvas.getGraphicsContext2D();
        gc.setLineWidth(currentLineWidth);
        gc.setStroke(currentColor);
        canvas.setOnMousePressed(this::handleMousePressed);
        canvas.setOnMouseDragged(this::handleMouseDragged);
        canvas.setOnMouseReleased(this::handleMouseReleased);
        root.setCenter(canvas);

        HBox toolbar = new HBox();
        toolbar.setPadding(new Insets(10));
        toolbar.setSpacing(10);
        ColorPicker colorPicker = new ColorPicker(currentColor);
        colorPicker.setOnAction(e -> {
            currentColor = colorPicker.getValue();
            gc.setStroke(currentColor);
        });
        Slider lineWidthSlider = new Slider(1, 20, currentLineWidth);
        lineWidthSlider.setShowTickLabels(true);
        lineWidthSlider.setShowTickMarks(true);
        lineWidthSlider.setMajorTickUnit(5);
        lineWidthSlider.setMinorTickCount(4);
        lineWidthSlider.setBlockIncrement(1);
        lineWidthSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            currentLineWidth = newValue.doubleValue();
            gc.setLineWidth(currentLineWidth);
        });
        Button clearButton = new Button("清除");
        clearButton.setOnAction(e -> {
            undoStack.push(canvas.snapshot(null, null));
            gc.clearRect(0, 0, canvas.getWidth(), canvas.getHeight());
        });
        Button undoButton = new Button("撤销");
        undoButton.setOnAction(e -> {
            if (!undoStack.empty()) {
                redoStack.push(canvas.snapshot(null, null));
                WritableImage undoImage = undoStack.pop();
                gc.drawImage(undoImage, 0, 0);
            }
        });
        Button redoButton = new Button("重做");
        redoButton.setOnAction(e -> {
            if (!redoStack.empty()) {
                undoStack.push(canvas.snapshot(null, null));
                WritableImage redoImage = redoStack.pop();
                gc.drawImage(redoImage, 0, 0);
            }
        });
        Button saveButton = new Button("保存");
        saveButton.setOnAction(e -> {
            FileChooser fileChooser = new FileChooser();
            fileChooser.setTitle("Save Image");
            fileChooser.getExtensionFilters().addAll(
                    new FileChooser.ExtensionFilter("PNG", "*.png"),
                    new FileChooser.ExtensionFilter("JPG", "*.jpg")
            );
            File file = fileChooser.showSaveDialog(primaryStage);
            if (file != null) {
                try {
                    WritableImage writableImage = new WritableImage((int) canvas.getWidth(), (int) canvas.getHeight());
                    canvas.snapshot(null, writableImage);
                    ImageIO.write(SwingFXUtils.fromFXImage(writableImage, null), "png", file);
                } catch (IOException ex) {
                    ex.printStackTrace();
                }
            }
        });
        toolbar.getChildren().addAll(colorPicker, lineWidthSlider, clearButton, undoButton, redoButton, saveButton);
        root.setTop(toolbar);

        VBox bottomBar = new VBox();
        bottomBar.setPadding(new Insets(10));
        bottomBar.setSpacing(10);
        Slider thicknessSlider = new Slider(1, 20, currentLineWidth);
        thicknessSlider.setShowTickLabels(true);
        thicknessSlider.setShowTickMarks(true);
        thicknessSlider.setMajorTickUnit(5);
        thicknessSlider.setMinorTickCount(4);
        thicknessSlider.setBlockIncrement(1);
        thicknessSlider.valueProperty().addListener((observable, oldValue, newValue) -> {
            currentLineWidth = newValue.doubleValue();
            gc.setLineWidth(currentLineWidth);
        });
        Button thinButton = new Button("Thin");
        thinButton.setOnAction(e -> {
            thicknessSlider.setValue(1);
        });
        Button mediumButton = new Button("Medium");
        mediumButton.setOnAction(e -> {
            thicknessSlider.setValue(5);
        });
        Button thickButton = new Button("Thick");
        thickButton.setOnAction(e -> {
            thicknessSlider.setValue(10);
        });
        bottomBar.getChildren().addAll(thicknessSlider, thinButton, mediumButton, thickButton);
        root.setBottom(bottomBar);

        primaryStage.setScene(new Scene(root));
        primaryStage.show();
    }

    private void handleMousePressed(MouseEvent e) {
        lastX = e.getX();
        lastY = e.getY();
    }

    private void handleMouseDragged(MouseEvent e) {
        gc.strokeLine(lastX, lastY, e.getX(), e.getY());
        lastX = e.getX();
        lastY = e.getY();
    }

    private void handleMouseReleased(MouseEvent e) {
        undoStack.push(canvas.snapshot(null, null));
    }

    public static void main(String[] args) {
        launch(args);
    }
}
